Gedurende de stage werkte ik voornamelijk aan de totstandkoming van een dataset binnen het project GLOBALISE bevattende plaatsen die personeel van de Vereenigde Oostindische Compagnie (VOC) noemde in zijn correspondentie. De dataset is bedoeld voor het annotatieproces van de transcripties van de correspondentie van de VOC. Daarnaast wil GLOBALISE de data publiceren, zodat andere onderzoekers er gebruik van kunnen maken. Mijn werkzaamheden brachten een aantal stage producten voort die ik hier opeenvolgend en tevens chronologisch behandel. De onderstaande afbeelding weergeeft de verschillende taken die leidden tot de uiteindelijke dataset.
Figuur 1. Proces en
product
De details van de werkprocessen die ik tijdens mijn stage bij het Huygens Instituut uitvoerde staan hieronder uitgelegd. Voor nu is het voldoende om te weten dat ik niet de enige ben die dit alles heeft gedaan. Product A en D zijn niet van mijn hand. Product A is een susbet van de dataset die Brecht Nijman heeft gemaakt en vormde de basis waarop ik voortborduurde. Deze dataset bevatte de vermeldingen van plaatsnamen zoals opgenomen in de publicaties van de Generale Missiven (GM) (Coolhaas et al. 1960) van Gouverneurs-Generaal en Raden aan de Heren XVII van de Verenigde Oostindische Compagnie, het directoraat. De dataset bevat ook de indexen van geografische namen in het Corpus Diplomaticum Neerlando-Indicum (CD) (Sholihah, Roling, and Gerbrandy 2019) waarin de contracten die de VOC met lokale heersers sloot zijn opgenomen. De dataset ziet er zo uit:
De dataset bevat informatie over de standaardnaam zoals in de GM
aangetroffen (standard_name_rgp_gm), een unieke plaatsnaam
(name) vergezeld door een ID, een alternatieve
naam in de CD en waar de desbetreffende plaats te vinden is in de GM of
CD.
Product D is door Leon van Wissen aangeleverd. Het is de ruwe data die ik gebruikte voor de webapplicatie, een van de stageproducten. De dataset bevat alle woorden die meer dan één maal voorkomen in een inventarisnummer van de Overgekomen Brieven en Papieren (OBP). De OBP zijn de documenten die VOC-personeel verzond naar de Nederlanden. De dataset bevatte de frequenties per woord per jaar, inclusief het originele inventarisnummer.
Hieronder beschrijf ik het werk dat ik heb gedaan. Telkens is een geschatte tijdsinvestering vermeld, waarna een beschrijving volgt, eindigende met het uiteindelijke product. Dit product is terug te vinden in de portfolio onder dezelfde naam. Ik neem alleen de belangrijke (eind-)producten op in de portfolio.
Geschatte tijdsinvestering: 15%
Ter indicatie is de uiteindelijke structuur van de dataset de volgende:
| Kolom | Beschrijving |
|---|---|
standard_name |
De standaardnaam is de vermelding in
standard_rgp_gm |
unique_name |
Elke unieke plaatsnaam, inclusief de vermeldingen in
standard_name en plaatsnamen uit Exploring Slave Trade in
Asia (ESTA) en de Atlas of Mutual Heritage (AMH) |
source |
De bron waarin een bepaalde unieke plaatsnaam (in
unique_name) voorkomt |
x_id |
Het ID van een standaardplaatsnaam (in standard_name)
binnen een bepaalde dataset x (GM/CD, AMH, ESTA, GeoNames) |
latitude |
De breedtegraad van een plaats (WGS 84) |
longitude |
De lengtegraad van een plaats (WGS 84) |
coord_source |
De bron van het coördinaat (AMH, ESTA of GeoNames) |
row_id |
Een uniek rij-ID voor elke rij in de vorm TEMP_x, indien aanwezig in de basisdataset, ADD_x als een plaatsnaam vervolgens door mij is toegevoegd |
coord_certainty |
Coördinaatzekerheid naar de volgende categorieën: automatic, certain, uncertain en approximate |
country_geofeature |
Huidige land waarin de plaats ligt |
state_province_region |
De staat, provincie of regio van het land in
country_geofeature |
De variabelen country_geofeature en
state_province_region zijn door Manjusha Kuruppath
aangemaakt.
Een van de eerste werkzaamheden was het opschonen van de dataset die
ik van Brecht kreeg. Eerst besloot ik om enkel een subset van de dataset
te gebruiken uit praktische overwegingen. Hij telt namelijk 1,555 rijen,
waardoor een keer werken met zulke tekstuele data niet behapbaar is.
Daarom selecteerde ik alleen de rijen met plaatsnamen die een
standaardplaatsnaam hebben in de GM, dus alle rijen met een observatie
in standard_name_rgp_gm. De uiteindelijke dataset heeft
namelijk ook een variabele standard_name, dus dit was een
goed startpunt.
Vervolgens is de dataset opgeschoond en geherstructureerd. Opschonen omvat het weghalen van onjuiste karakters, zoals een ‘?’ die in de oorspronkelijke bron een ‘ij’ is, maar ook het wegfilteren van adelijke titels, zoals ‘Atjeh, koningin van’. Ook duplicaten binnen één cel zijn gecorrigeerd, zodat deze maar één ervan bevat.
Daarna herstructureerde ik de data. De name kolom is
aangewezen als de kolom voor de unieke plaatsnamen die later omgedoopt
zou worden tot unique_name.
standard_name_rgp_gm hermoemde ik later tot
standard_name. Daarom zijn spelvarianten in
alternate_name_cd verplaatst naar de kolom met de unieke
namen, zodat alle spelvarianten in één kolom staan. Ik maakte ook een
variabele die elke plaats een ID gaf. Zo
Al deze activiteiten vereisten dat ik in Python programmeerde. Het was fijn hiermee te beginnen, want ik had al een tijdje niet meer geprogrammeerd in deze taal en de benodigde code was relatief makkelijk. Ondertussen begon ik ook met het schrijven van documentatie. Dit was de eerste keer dat ik documentatie schreef. Ik leerde hierbij wat ik moest vermelden in een documenatie. Eveneens vroeg het van mij om precies te zijn over de werkzaamheden, zodat anderen het werk kunnen evalueren en repliceren. Daarnaast bood het mij de kans om na te denken over wat ik deed.
plaatsnamendataset.xlsx,
plaatsnamendataset_documentatie.html,
plaatsnamendataset_datasheet.pdfGeschatte tijdsinvestering: 20%
De dataset is op zichzelfstaand echter minder interessant dan wanneer er ook links gemaakt worden met andere datasets. Op die manier kunnen onderzoekers data uit verschillende datasets gebruiken. Daarnaast kan de GLOBALISE dataset data lenen uit andere datasets. Daarom is ervoor gekozen om directe links te maken met de dataset Exploring Slave Trade in Asia (ESTA) door Pascal Konings (Konings 2023) en de Atlas of Mutual Heritage (AMH), wat een doorlopend project is door een aantal organisaties, waaronder het Nationaal Archief (Nationaal Archief, Rijksdienst het Cultureel Erfgoed, and Rijksmuseum Amsterdam and the Koninklijke Bibliotheek 2023). Deze datasets bevatten veel relevante historische geografische data, omdat ze grofweg dezelfde periodisering en geografische afbaking hebben als GLOBALISE.
Eerst maakte ik een match met ESTA. Hiertoe werd een exacte match gezocht tussen een unieke plaatsnaam in GLOBALISE’s dataset en die van ESTA, waarna het ID van ESTA toegevoegd werd aan de dataset. Bovendien voegde ik nieuwe rijen toe met spellingsvarianten uit ESTA die nog niet waren opgenomen in de plaatsnamendataset. Daarvoor creëerde ik de volgende functie:
import pandas as pd
# Append original names to a data frame
def append_original_places(main_dataframe, new_places_dataframe, places_col):
# Make a list for original rows
new_rows = []
# Iterate over rows of data frame with new places
for index, row in new_places_dataframe.iterrows():
# Check if the name does not appear in main data frame
if main_dataframe[places_col].eq(row[places_col]).any() == False:
# Append to the list with new rows
new_rows.append(row)
else:
pass
# Concatenate the main dataframe and the list with new names
main_dataframe = pd.concat([main_dataframe, pd.DataFrame(new_rows, columns = main_dataframe.columns)], axis = 0)
return main_dataframe
De functie vraagt twee datasets en de kolomsnaam waarin de plaatsen staan. Deze naam moet voor beide datasets gelijk zijn. In dit geval gaf ik de de plaatsnamendataset en de dataset met de plaatsen uit ESTA die ook in de GLOBALISE plaatsnamendataset zijn, inclusief het bijhorende GLOBALISE ID. Vervolgens zijn de rijen uit de tweede dataset toegoegd aan de eerste, indien de unieke naam uit ESTA nog niet opgenomen was in GLOBALISE’s dataset. De AMH onderging dezelfde handelingen, maar onderscheidt zich van ESTA in dat eerst de plekken in Amerika uit de dataset gefilterd moesten worden. De AMH bevat namelijk observaties voor zowel de VOC als de Westindische Compagnie. Dit was niet lastig, want de AMH heeft een variabele met daarin het huidige land waarin een plaats lag.
Aangezien de matches gebaseerd zijn op de plaatsnamen uit de
datasets, zijn er fouten gemaakt. Het komt namelijk met regelmaat voor
dat één plaats in de dataset van GLOBALISE meerdere ESTA of AMH ID’s
heeft, omdat er meerdere plaatsen met dezelfde naam zijn. Een voorbeeld
hiervan is Juda. Daarom schreef ik de functie
id_confusion() om die gevallen te markeren:
import pandas as pd
import numpy as np
# Function that checks whether an ID in one data set contains multiple ID's in another
# Supply data frame, grouping ID, ID of which instances are to be count,
# and a variable that indicates the existence of multiple ID's
def id_confusion(dataframe, reference_id, check_id, confusion_var_name):
# Create a data frame with a row per reference ID and a list of unique ID's of another data frame
id_set = pd.DataFrame(dataframe.groupby(reference_id)[check_id].apply(lambda x: set(x)).apply(lambda x: list(x))).reset_index()
# Create a list for the counter and to indicate
# if there is 'ID confusion' (i.e. multiple ID's in one data set, but one in another)
ID_count = []
confusion_list = []
for index, row in id_set.iterrows():
# Filter out empty elements in the list
row[check_id] = list(filter(None, row[check_id]))
# Count elements and appent to list
ID_count.append(len(row[check_id]))
for count in ID_count:
# If more than one ID accompanying the reference ID:
if count > 1:
# Append 1 = "yes"
confusion_list.append(1)
else:
# Else 0 = "no"
confusion_list.append(0)
# Append the list to the data frame created above
id_set[confusion_var_name] = confusion_list
id_set = id_set[id_set[check_id].map(lambda id_list: len(id_list)) > 0]
id_set[reference_id].replace('', np.nan, inplace = True)
id_set.dropna(subset = [reference_id], inplace = True)
return id_set
De gebruiker geeft de data set mee, waarbij hij of zij aangeeft welk
ID als referentiekader dient en waartegen het andere ID vergeleken moet
worden. Het resultaat is een dataframe met het GLOBALISE ID en het ID
waartegen vergeleken wordt met daarbij de waarde 1 als er
verwarring is over het ID en 0 indien dit niet het geval
is. Djeddah heeft daarom de waarde 1, want het GLOBALISE ID
heeft twee AMH ID’s:
| unique_name | standard_name | glob_id | source | amh_id | glob_amh_confusion |
|---|---|---|---|---|---|
| Juda | Djeddah | GLOB_156 | GM/CD, AMH | amh_918p | 1 |
| Djeddah | Djeddah | GLOB_156 | GM/CD, AMH | amh_752p | 1 |
| Ouidah | Djeddah | GLOB_156 | AMH | amh_918p | 1 |
| Fida | Djeddah | GLOB_156 | AMH | amh_918p | 1 |
| Whydah | Djeddah | GLOB_156 | AMH | amh_918p | 1 |
| Hueda | Djeddah | GLOB_156 | AMH | amh_918p | 1 |
| Whidah | Djeddah | GLOB_156 | AMH | amh_918p | 1 |
| Jiddah | Djeddah | GLOB_156 | AMH | amh_752p | 1 |
| Sjedda | Djeddah | GLOB_156 | AMH | amh_752p | 1 |
| Cidade de Iudda | Djeddah | GLOB_156 | AMH | amh_752p | 1 |
Deze variabele diende enkel om handmatige correcties later te vergemakkelijken. Ze is niet opgenomen in de uiteindelijke dataset.
Tot slot verrijkte ik ook de dataset met de ID’s van de geografische
databank GeoNames die geografische data bevat over hedendaagse plaatsen.
Uiteindelijk zijn de GeoNames ID’s overgenomen die al door de AMH
gebruikt waren, maar in de eerste instantie wilde ik zelf de API van
GeoNames aanroepen. Daarvoor schreef ik de functie
geonames_matcher:
import geocoder
import pandas as pd
import numpy as np
# Function to match names with GeoNames based on their name and a designated coordinate space
# Requires the geocoder module
# Input: dataframe containing placenames and coordinates; how many degrees in each direction has to be searched;
# variable names for place names, latitudes, longitudes, feature classes and codes; account name of GeoNames;
# Previous dataframe (GeoNames only processes a limited amount of input an hour, so the result has to be stored
# to process the remainder at another moment)
DEFAULT = object()
def geonames_matcher(coords_dataframe, degrees, place_var, lat_var, long_var, feature_classes, feature_codes, geo_acc, prev_df = DEFAULT):
output_dataframe = pd.DataFrame({'name': pd.Series(dtype='str'),
'country': pd.Series(dtype='str'),
'geonames_id': pd.Series(dtype='int'),
'matched_on': pd.Series(dtype = "str")})
# Concatenate previous data frame to the empty new one, if there is one
# You need this, because of the limited number of requests you can give to GeoNames
if prev_df is not DEFAULT:
output_dataframe = pd.concat([output_dataframe, prev_df]).reset_index(drop = True)
for index, row in coords_dataframe.iterrows():
if (row[lat_var]) and (row[place_var] not in output_dataframe["matched_on"].unique()):
# Extract the place name
place = row[place_var]
# Add and subtract three points to and from the coordinates to make a surface
north = row[lat_var] + degrees
south = row[lat_var] - degrees
east = row[long_var] + degrees
west = row[long_var] - degrees
bbox = [west, south, east, north]
# Search for the place in geo names, within the given surface
geo = geocoder.geonames(place, featureCodes = feature_codes, \
featureClasses = feature_classes, maxRows = 1, proximity = bbox, key = geo_acc)
status = geo.status
# Break if the limit is exceeded
if "the hourly limit of 1000 credits for {account} has been exceeded".format(account = geo_acc) in status:
break
# Add as a row in the geonames dataframe
new_row = pd.DataFrame({"name": geo.address,
"country": geo.country,
"geonames_id": geo.geonames_id,
"matched_on": place}, index=[0])
output_dataframe = pd.concat([output_dataframe.loc[:], new_row]).reset_index(drop = True)
elif not row[lat_var]: # Append place name without coordinates to the data frame
place = row[place_var]
new_row = pd.DataFrame({"name": np.nan,
"country": np.nan,
"geonames_id": np.nan,
"matched_on": place}, index=[0])
output_dataframe = pd.concat([output_dataframe.loc[:], new_row]).reset_index(drop = True)
return output_dataframe
Deze functie is dus niet gebruikt, maar ik wil er hier kort aandacht
aan besteden, omdat het de meest uitgebreide functie is die ik tot nu
toe geschreven had. De functie neemt een dataset met de lengte- en
breedtegraadvariabelen, waarna ze zoekt naar de plaats met dezelfde naam
binnen de bounding box die de gebruiker maakt door een aantal
degrees door te geven. Verder zijn er een paar andere
variabelen die de gebruiker kan meegeven. Met betrekking tot de
doorzoeking heeft GeoNames echter een limiet voor de hoeveelheid
informatie die per uur met een gratis account opgevraagd kan worden.
Daarom moest de data tussentijds opgeslagen (i.e. gepickeld) worden om
daarna de overige rijen door GeoNames te halen ter matching. De
voortgang tot dan toe kan aan de functie gegeven worden met het argument
prev_df, wanneer de gebruiker de functie weer uitvoert. Het
uiteindelijke resultaat is een dataframe met daarin de naam van de
plaats met het bijbehorende land en GeoNames ID. Daarnaast is te zien
wat de match was tussen de GLOBALISE dataset en GeoNames.
Tot slot voegde ik nog coördinatent toe, aangezien ESTA en de AMH die voor veel plekken hadden. Ondertussen werkte ik steeds de documentatie bij.
plaatsnamendataset.xlsx,
plaatsnamendataset_documentatie.html,
plaatsnamendataset_datasheet.pdfGeschatte tijdinvestering: 25%
Nadat de basis voor de dataset gelegd was, was het tijd om de dataset handmatig op te schonen. Dit kostte veel tijd, omdat er veel observaties waren die op de een of andere manier aangepast moesten worden. Correcties bestonden uit:
Incorrecte matches tussen GLOBALISE, ESTA en de AMH corriggeren;
Plaatsen die meer dan één GLOBALISE ID terugbrengen tot één ID;
Plaatsnamen differentiëren die één GLOBALISE ID hadden, maar eigenlijk naar verschillende plaatsen verwijzen;
Plaatsnamen die in identieke vorm in meer dan één rij voorkwamen terugbrengen tot één rij (between double);
Plaatsnamen die in identieke vorm in dezelfde rij in dezelfde cel voorkwamen terugbrengen tot één plaatsnaam (within double);
Bevolkingsgroepen en adelijke titels verwijderen, indien deze nog niet tijdens de semi-automatische opschoning opgespoord waren.
Alle wijzigingen zijn bijgehouden in een logboek. Daarnaast is de data van voor en na de correcties nog beschikbaar. Na de handmatige correcties heb ik voor enkele plaatsen handmatig coördinaten en GeoNames ID’s toegevoegd, maar er was niet voldoende tijd om dit voor veel plekken te doen. Alle werkprocessen zijn tevens omschreven in de documentatie en in een datasheet. De overlap tussen de documentatie en de datasheet is groot. Een datasheet is er voor bedoeld om een mogelijke gebruiker op de hoogte te stellen over de aard van een dataset en de bijbehorende sterke en zwakke punten. Het datasheet dat ik maakte volgt een vraag-en-antwoordstructuur.
De uiteindelijke dataset heeft de volgende vorm:
Zoals eerder gezegd zijn de variabelen
country_geofeature en state_province_region
zijn dus niet van mijn hand. De exacte betekenis per variabele is te
vinden in de tabel onder 1. Semi-automatische opschoning.
Dit was de eerste versie van de dataset, maar hij kon meteen ingezet worden voor het maken van een ander stageproduct, namelijk de interactieve webapplicatie waarmee een gebruiker de plaatsnamen binnen de OBP kan verkennen. Daarvoor was echter eerst het corpus van de OBP nodig. Deze leverde Leon van Wissen aan. Vervolgens filterde ik de plaatsnamen uit de OBP, indien er een exacte match was tussen de plaats in de OBP en een plaatsnaam in de plaatsnamendataset. De plaatsnamendataset is echter slechte een selectie door een meervoud aan redenen. Lees de documentatie, het datasheet en stageverslag voor meer informatie hierover. Eén van de redenen is bijvoorbeeld dat ik werkte met een subset van de indexen van de GM.
De data die ik voor de applicatie gebruikte ziet er zo uit:
Met deze data kon het werk aan de app beginnen!
plaatsnamendataset.xlsx,
plaatsnamendataset_documentatie.html,
plaatsnamendataset_datasheet.pdfGeschatte tijdsinvestering: 35%
Anders dan bij de semi-automatische opschoning maakte ik hier gebruik van R. Waar Python een taal is die voor vele doeleinden gebruikt kan worden, is R ontworpen voor data-analyse en statistiekbeoefening. Binnen R is de mogelijkheid om interactieve webapplicaties te bouwen met de package Shiny. Nu is dit ook mogelijk in Python, maar ik koos voor R, omdat ik daar ervaring mee heb.
Aan de basis van de app liggen twee scripts en natuurlijk de data. Het ene script is voor de gebruikersinterface, het andere voor de server.
Het voert te ver om in detail in te gaan op de code, maar om een indicatie te geven is de volgende code gebruikt om de kaart te creëren. Aan de zijde van de gebruikersinterface:
tabPanel(title = "Mapping history",
actionButton(
"fullscreen", "Full screen version",
onclick = "openFullscreen(document.getElementById('map_container'))"
),
div(
id = "map_container",
leafletOutput(height = "950px", "map"),
absolutePanel(
top = 90,
right = 20,
style = "color: #FFF",
h3("Parameters", style = "color:white"),
# Defining colors for the sliders
setSliderColor(c("#ff0055", "#33a9ac"), c(1, 2)),
# Slider for determining the period of the map
sliderInput("year_map", "Period", min(df$year), max(df$year),
value = range(df$year), step = 1, sep = ""),
h4("Enter places to calculate the shortest distance"),
textInput("input_place1", "First place", "Cadiz"),
textInput("input_place2", "Second place", "Roti"),
textOutput("distance"),
h5(HTML("Attested HTR OBP places with amount of mentions. <br><br>
Distances are calculated using places names present <br>
in the specified period. <br><br>
Calculation method: the geodesic of the WGS84 ellipsoid"))
)
),
tags$scrip(HTML(js))
)
De bovenstaande code creëert een knop waarmee de gebruiker de kaart
op het volledige scherm kan weergeven. Vervolgens wordt een
map_container aangemaakt die de kaart bevat
(leafletOutput()) en de verschillende widgets die de
gebruiker kan manipuleren, zoals de slider (year_map) voor
de periode en twee balken voor tekstinvoer (input_place1 en
input_place2). Achter de gebruikersinterface ligt de
server:
# Render the map
output$map <- renderLeaflet({
leaflet(data = df_map, options = list(worldCopyJump = TRUE)) %>%
# Choosing a nice background: terrain
addProviderTiles(providers$Stamen.TerrainBackground,
options = providerTileOptions(noWrap = FALSE,
minZoom = 3,
maxZoom = 11)) %>%
# Set the first view
setView(lng = 128, lat = -4, zoom = 3)
})
updateSelectizeInput(session, "input_place1", choices = unique(df$standard_name))
observe({
# Plot the line if two places are filled in by the user, otherwise no line
if (input$input_place1 %in% df_filtered_map()$standard_name &
input$input_place2 %in% df_filtered_map()$standard_name) {
# Calculate the distance between the two given places
distance <- distm(c(coords()[1, "longitude"], coords()[1, "latitude"]),
c(coords()[2, "longitude"], coords()[2, "latitude"]),
fun = distHaversine)
# Add circles for the map based on filter
leafletProxy("map", data = df_filtered_map(), session = session) %>%
clearShapes() %>%
addCircles(lng = ~longitude, lat = ~latitude,
popup = ~paste0("place: ", standard_name, ", mentions: ", frequency,
", GLOBALISE ID: ", sub("GLOB_", "", glob_id)),
color = "#ff0055",
# Circle size also depends on zoom level
radius = ~frequency*3*(1/input$map_zoom)) %>%
addGeodesicPolylines(data = coords(), lng = ~longitude, lat = ~latitude,
group = ~standard_name, color = "#ff0055",
steps = 10000, opacity = 0.50,
smoothFactor = 0.75, weight = 3.5,
popup = ~paste0("The shortest distance between ", input$input_place1,
" and ", input$input_place2,
" is ", as.integer(distance/1000), " km"))}
else {
leafletProxy("map", data = df_filtered_map(), session = session) %>%
clearShapes() %>%
addCircles(lng = ~longitude, lat = ~latitude,
popup = ~paste0("place: ", standard_name, ", mentions: ", frequency,
", GLOBALISE ID: ", sub("GLOB_", "", glob_id)),
color = "#ff0055",
radius = ~frequency*3*(1/input$map_zoom))
}
})
# Output text for the distance between two places
output$distance <- renderText({if (input$input_place1 %in% df_filtered_map()$standard_name &
input$input_place2 %in% df_filtered_map()$standard_name) {
paste0("Click the line for the distance between ", input$input_place1 ,
" and ", input$input_place2)
} else {
paste0("Incorrect input")
}
})
Het eerste gedeelte rendert de kaart. Daarna volgt het script dat de
lijn tussen twee door de gebruiker gespecificeerde plekken plot en de
afstand berekend. De lijn houdt rekening met het feit dat de wereld op
een cilinder is geprojecteerd in het geval van de mercatorprojectie.
Daarom is de lijn niet recht. Je kan de lijn alleen plotten als de
plaatsen geobserveerd zijn binnen de door de gebruiker bepaalde periode
met de slider (year_map). De coördinaten worden dus uit een
gefilterde dataset gehaald (df_filtered_map()) Voor die
berekening zijn coördinaten nodig die als volgt zijn opgevraagd:
coords <- reactive({
if (input$input_place1 %in% df_filtered_map()$standard_name &
input$input_place2 %in% df_filtered_map()$standard_name) {
data.frame(filter(df_filtered_map(), standard_name %in% c(input$input_place1, input$input_place2)))
}
})
De app geeft een melding indien de plaats die de gebruiker opgeeft
niet in df_filtered_map() zit. Eveneens bepalen de selectie
van de periode en het zoomniveau de grootte van de cirkels. De cirkels
weerspiegelen de hoeveelheid vermeldingen van een bepaalde plaats binnen
de gegeven periode. Het resultaat is een interactieve kaart (Figuur
2)!
Figuur 2. Interactieve
kaart
De gehele applicatie met de andere visualiseringen een een korte introductie en documentatie zijn online te vinden en is te runnen vanuit de portfolio.
mapping_places. Het is ook mogelijk de app lokaal te runnen
indien RStudio op de computer staat.Geschatte tijdsinvestering: 5%
Tot slot had ik de gelegenheid om de bovenstaande producten te presenteren tijdens een datasprint. Deze activiteit heb ik niet opgenomen in Figuur 1, omdat het relatief weinig werk was, maar is desondanks noemenswaardig. De datasprint had als thema het verzamelen van data over plaatsen uit oude kaarten en het koppelen van verschillende plaatsnamendatasets aan elkaar via een historisch-geografische databank genaamd de World Historical Gazetteer. Hieronder is de presentatie te zien die ik gaf (vanaf Places all around a large part of the world in the VOC archives):
De presentatie gaat in op de dataset en de applicatie, zich focussende op de sterke en zwakke kanten van de dataset en welke bijdrage de datasprint kan leveren om de creatie van plaatsnamendatasets te vergemakkelijken. Een voorbeeld is dat nu nog veel coördinaten ontbreken, maar dat het extraheren van coördinaten uit oude kaarten door ze op een moderne kaart te leggen een oplossing voor deze uitdaging zou kunnen bieden. Na de presentatie nam ik deel aan de sessie voor de omvorming van de data naar een format dat de World Historical Gazetteer accepteert.
plaatsnamendataset_presentatie.pdfTijdens mijn stage heb ik de mogelijkheid gekregen om veel te leren over werken met data en de mogelijke valkuilen die zich voordoen daarbij. Het documenteren van mijn werk was daarom van groot nut, omdat het mij liet nadenken over wat ik deed en hoe dat de representativiteit van de data beïnvloedde. Denk daarbij bijvoorbeeld aan de steekproeftrekking. Vooral het datasheet hielp bij de reflectie, omdat deze al vaststaande vragen bevatte die ik vervolgens moest beantwoorden. De app liet mij weer op een heel andere wijze naar de data kijken, namelijk hoe ik deze het beste kan visualiseren. Aanvankelijk was het niet gepland dat er een presentatie zou komen, dus ik was blij dat die gelegenheid er was, zodat ik ook aan mijn presentatievaardigheden kan werken. Om deze redenen denk ik dat de dataset, documentatie, datasheet, applicatie en de presentatie de stageproducten zijn die een mooi beeld schetsen van mijn bezigheden.